ex7_5

#include<string>

class Person
{
private:
	std::string name;
	std::string address;
public:
	std::string getName() const { return name; };
	std::string getAddress() const { return address; };
};

这些函数应该是const类型的，因为无论是返回姓名还是地址，都不需要改变内容，所以将其定义成const类型的

ex7_8

因为我们需要从标准输入流中读取数据并将其写入到给定的Sales_data对象，因此需要有修改对象的权限，
而print将其参数定义成常量引用是因为它只负责数据的输出，不对其做任何的改变


ex7_10

因为read函数的返回类型是引用类型，所以read(cin,data1)的返回值可以继续作为外层read函数的实参来使用。该条件检验读入的data1和data2的过程是否正确，如果正确，则条件满足，否则，条件不满足

ex7_14

使用初始值列表的构造函数是：
  Sales_data(const std::string &book)
  	:bookNo(book),units_sold(0),sellingprice(0),saleprice(0),discount(0){ }

ex7_15
一个类可以包含0个或多个访问说明符，而且对于访问说明符能出现多少次也没有限定；作为接口的一部分，构造函数和一部分成员函数应该定义在public说明符之后，而数据成员和作为实现的部分函数应该紧跟在private说明符之后

ex7_16

class 和 struct都可以用来声明类，它们大部分功能都相似，根本区别在于：
   使用class时，类中的成员都默认是private属性的，而使用struct时，结构体中的成员默认都是public属性的
   
ex7_17

封装、继承、多态是类的三个特性；封装简单来说就是隐藏了类的实现细节，即封装保护类的成员不能被随意访问的能力，
封装实现了类的接口和实现的分离，隐藏了类的实现细节，用户只能接触到类的接口。

优点：

隐藏类的实现细节；
让使用者只能通过程序规定的方法来访问数据；
可以方便的加入存取控制语句，限制不合理操作；

类自身的安全性提升，只能被访问不能被修改；

类的细节可以随时改变，不需要修改用户级别的代码；

ex7_19


 根据C++的规定：作为接口的一部分，构造函数和一部分成员函数应该定义在public说明符之后，而数据成员和作为实现的部分函数应该紧跟在private说明符之后
 所以在Person类中，构造函数：
 	Person() = default;
	Person(const std::string &sname, const std::string saddress) :name(sname), address(saddress) { }
	Person(std::istream &is) { read(is, *this); }
和部分成员函数：
	std::string getName() const { return name; }
	std::string getAddress() const { return address; }
应该放在public访问说明符之后；
数据成员：
	std::string name;
	std::string address;
放在private访问说明符之后；

当非成员函数需要访问类的私有成员时，我们可以把它声明成该类的友元
优点：此时友元工作在类的内部，可以像类的成员一样访问类的所有数据和函数
缺点：如果使用不好，就可能破坏类的封装性

ex7_25

含有指针数据成员的类一般不宜使用默认的拷贝和赋值操作，如果类的数据成员都是内置类型的，则不受干扰，即只有内置类型和string类型可以依赖于操作的默认版本,因为Screen类中的数据成员都是内置类型的，所以可以安全地依赖于拷贝和赋值操作的默认版本

ex7_28

如果move、set和display函数的返回类型不是Screen&而是Screen，那么因为返回类型不是引用，所以move、set和display函数的返回值将是this解引用对象的副本，因此调用set只能改变临时副本，并不能改变myscreen的值
官方答案：
      返回引用的函数是左值的，意味着这些函数返回的是对象本身而非对象的副本。如果我们把一系列这样的操作连接在一起的话，所有这些操作将在同一个对象上执行。  
      在上一个练习中，move、set和display函数的返回类型都是Screen&，表示我们首先移动光标到（4,0）位置，然后将该位置的字符修改为‘#’，最后输出myScreen的内容
      想法，如果我们把move、set和display函数的返回类型改成Screen，则上述函数各自只返回一个临时副本，不会改变myScreen的值
  
      
ex7_29

经过验证，推断是正确的

ex7_30

显示地使用this指针访问成员既有优点又有缺点：
优点是：
	1.当需要将一个对象作为整体引用而不是引用对象的一个成员时，使用this，则该函数返回对调用该函数的对象的引用。 
	2.可以非常明确地指出访问的是调用该函数的对象的成员，且可以在成员函数中使用与数据成员同名的形参。  

缺点：不必要使用，代码多余。

ex7_33

如果给Screen添加了这样的一个size成员，那么编译器将会报错，因为编译器不知道pos是来自哪的，因此如果想在类的外部使用pos，需要在它的前面加上作用域来说名，即应该添上Screen::,修改后的成员函数为：
    Screen::pos Screen::size() const
    {
    	return height*width;
    }

ex7_34

因为在类中，名字的查找是自上而下，由内到外的，因此如果把Screen类的pos的typedef放在类的最后一行，那么将出现pos未定义的错误，会报错

ex7_35

#include<iostream>
#include<string>

typedef string Type;   //声明类型别名Type表示string
Type initVal();    //声明一个函数initVal，返回类型是Type
class Exercise
{
public:
	typedef double Type;   //在内层作用域中重新声明类型别名Type表示string
	Type setVal(Type);     //声明函数setVal,函数的参数和返回值类型都是Type
	Type initVal();        //在内层作用域重新声明函数initVal，返回类型是Type

private:
	int val;         //声明私有数据成员val
};
//定义函数setVal，此时的Type是外部作用域的
Type Exercise::setVal(Type parm)
{
	val = parm + initVal();   //此处使用的是类内的initVal函数
	return val;
}
其中，在Exercise类的内部，函数setVal和initVal用到的Type都是Exercise类内部声明的类型别名，对应的实际类型是double类型的
在Exercise类的外部，定义Exercise::setVal函数时形参类型Type用的是Exercise内部定义的别名，对应double；返回类型Type用的是全局作用域的别名，对应string。使用的initVal函数是Exercise类内定义的版本。
    编译上述程序在setVal的定义处发生错误，此处定义的函数类型是double，但是返回值的类型是string，不匹配，所以应该在定义setVal函数时使用作用域运算符强制指定函数的返回值类型：
      Exercise::Type Exercise::setVal(Type parm)
      {
      	val=parm+initVal();   //此处使用的是类内的initVal
	return val;
      }

ex7_36

这段程序首先声明了rem，然后再声明base，因此X(int i,int j):base(i),rem(base%j){ }中，实际上是先初始化rem(base%j)，但此时base还没有定义，因此这段程序是错误的。所以要进行如下的修改：
	struct X{
		X(int i,int j):base(i),rem(base%j){ }
		int base,rem;
	};

ex7_37

Sales_data first_item(cin);   //使用了接受std::istream参数的构造函数Sales_data(std::istream &is) { read(is, *this); },该对象的成员值依赖于用于的输入；
Sales_data next;   //使用了默认构造函数Sales_data() = default;
Sales_data last("9-999-99999-9");//使用了接受const string&参数的构造函数Sales_data(const std::string &s) :bookNo(s) { }，其中实参初始化为“9-999-99999-9”，其他几个成员使用类内初始值初始化为0

ex7_38

由题意应该写成：
	Sales_data(std::istream &is=std::cin){is>>*this;}
	
ex7_39

不合法，因为这样编译器就不知道该调用哪个参数进行初始化了；
官方答案：如果我们为构造函数的全部形参都提供了默认实参（包括为只接受一个形参的构造函数提供默认实参），该构造函数同时具备了默认构造函数的作用，此时即使我们不提供任何实参地创建类的对象，也可以找到可用的构造函数
        然而，如果按照本题的叙述，我们为两个构造函数同样都赋予了默认实参，则这两个构造函数都具有了默认构造函数的作用，一旦我们不提供任何实参地创建类的对象，则编译器无法判断这两个（重载的）构造函数到底哪一个更好，从而出现了二义性错误

ex7_40

我就和答案选择一样的吧，选择(a)Book，该类需要的数据成员有：BookName，ISBN,Author，Publisher，Price
#include<iostream>
#include<string>


class Book
{
public:
	Book() = default;
	Book(const std::string &name, const std::string &I, double pr, const std::string &a, const std::string &p)
	{
		BookName = name;
		ISBN = I;
		Price = pr;
		Author = a;
		Publisher = p;
	}
	Book(std::istream &in)
	{
		in >> BookName >> ISBN >> Price >> Author >> Publisher;
	}

private:
	std::string BookName, ISBN, Author, Publisher;
	double Price = 0.0;
};

ex7_42

首先，含有5个参数的那个函数不应该是委托构造函数，另外两个可以做构造函数，即

#include<iostream>
#include<string>


class Book
{
public:

	Book(const std::string &name, const std::string &I, double pr, const std::string &a, const std::string &p)
	{
		BookName = name;
		ISBN = I;
		Price = pr;
		Author = a;
		Publisher = p;
	}
	Book():Book("","",0,"",""){ }
	Book(std::istream &in) :Book() { in >> BookName >> ISBN >> Price >> Author >> Publisher; }

private:
	std::string BookName, ISBN, Author, Publisher;
	double Price = 0.0;
};

ex7_44

这条声明是非法的：这条声明的含义是创建一个vector对象vec，该对象包含10个元素，每个元素的类型都是Nodefault，并且执行默认初始化，但是因为没有在类NoDefault的定义中设计默认构造函数，所以所需的默认初始化过程无法执行，编译器会报错

ex7_45

如果定义的vector的元素类型是C，则声明就是合法的了，因为类C中定义了带参数的默认构造函数，可以完成声明语句所需的默认初始化操作

ex7_46

(a)是错误的，类可以不提供任何构造函数，此时编译器会自动生成一个合成的默认构造函数；
(b)是错误的，类通过一个特殊的构造函数来控制默认初始化的过程，这个函数叫做默认构造函数，而不是说参数列表为空的函数就是默认构造函数；
   因为如果某个构造函数包含若干形参，但是同时为这些形参都提供了默认实参，则该构造函数也具备默认构造函数的功能
(c)是错误的，因为如果一个类没有默认构造函数，那么当编译器隐式地为该函数创建一个默认构造函数的时候，这个类是无法使用的，所以一般情况下，都应该为类构建一个默认构造函数
(d)是错误的，对于编译器合成的默认构造函数来说，类类型的成员执行各自所属类的默认构造函数，内置类型和复合类型的成员只对定义在全局作用域中的对象执行初始化


ex7_47

explicit的作用是用于抑制类类型的隐式转换，所以接受一个string参数的Sales_data构造函数应该是explicit的，因为这样可以防止string对象自动隐式地转换成Sales_data对象，这种做法不是很严谨，某些时候回和程序员的想法不一致

explicit的优缺点
优点：可以避免因为隐式类型转换而带来的意想不到的错误
缺点：当用户需要这样的类类型转换时，需要使用繁琐的方式来实现

ex7_48
这段代码中：第一行创建了一个string对象，第二行和第三行都是调用了Sales_data的构造函数创建它的对象。此处无需任何类类型转换，因为都显式地声明了属于Sales_data类，所以不论是item1和item2都能被准确地创建，他们的bookNo成员都是9-999-99999-9，其余成员都是0


ex7_49

（a）是正确的，编译器首先用给定的string对象自动创建一个Sales_data对象，然后这个新生成的临时对象传给combine的形参，函数正确执行并返回结果
（b）无法编译通过，因为combine函数的参数是一个非常量引用，而s是一个string对象，编译器自动创建的Sales_data临时对象无法传递给combine函数所需要的非常量引用，所以编译失败，应进行如下修改：
	Sales_data &combine(const Sales_data&)
（c）无法编译通过，因为该函数声明中含有const，即combine是一个常量成员函数，无法修改数据成员的值

ex7_50

因为关键字explicit只对一个实参的构造函数有效，所以Person类中可以添加explicit关键字的构造函数有：
	explicit Person(std::istream &is) { read(is, *this); }
	
ex7_51

定义为explicit是为了防止隐式的类型转换,string接受的单参数是const char*，即常量字符类型，所以如果我们得到了一个常量字符指针，则把它看成string对象时自然而然的过程，编译器会自动把参数类型转换成类类型，所以无需指定explicit；
vector接受的单参数是int类型，这个参数的原意是指定vector的容量，所以我们在本来需要vector的地方提供一个int值并且希望这个int值自动的转换成vector，则这个过程显得比较牵强，因此把vector的单参数构造函数定义成explicit更加合理

ex7_52

因为聚合类要求没有类内初始值，所以题目中所给的这段程序片段是错误的，应该去掉初始值，如下所示：
	struct Sales_data
{
	std::string bookNo;
	unsigned units_sold;
	double revenue;
};

ex7_54

以set开头的成员不应该加上constexpr，因为constexpr函数必须有返回值，必须有return语句，而set开头的那些成员都是void，即没有返回值，所以不能加上constexpr

ex7_55

因为根据定义，数据成员都是字面值类型的聚合类是字面值常量类.Data的数据成员都是字面值类型的，所以Data类是字面值常量类

ex7_56

在成员之前加上关键字static使得它和类关联在一起，这样的成员叫做类的静态成员
静态成员的优点：
作用域位于类的范围之内，避免与其他类的成员或者全局作用域的名字冲突；
可以是私有成员，而全局对象不可以；
通过阅读程序可以看出静态成员与特定类关联，使得程序的含义清晰；

与普通成员的区别：
普通成员与类的对象关联，是某个具体对象的组成部分，静态成员不属于任何具体的对象，它由该类的所有对象共享；
静态成员可以作为默认实参，而普通数据成员不能作为默认实参

ex7_58

这段程序是有语法错误的：
首先，静态函数的定义必须在类的外部，而不能在类的内部，所以rate和vecSize的定义应该放在类的外部；
其次，在example.c文件中，必须给出静态成员的初始值，即rete和vec应该是有初始值的
